import { ArrayType, EPSILON } from "./common";
/**
 * 三维向量类
 * @class
 * @author jsbrml
 */
class Vec3 extends ArrayType {
    /**
     * 构造函数。
     * @constructor
     */
    constructor(element = [0, 0, 0]) {
        super(element);
    }
    /**
     * @property  {Number} x 
     * 【读写】获取向量的x分量。
     */
    get x() {
        return this[0];
    }
    set x(v) {
        this[0] = v;
    }
    /**
     * @property  {Number} y 
     * 【读写】获取向量的y分量。
     */
    get y() {
        return this[1];
    }
    set y(v) {
        this[1] = v;
    }
    /**
     * @property  {Number} z
     * 【读写】获取向量的z分量。
     */
    get z() {
        return this[2];
    }
    set z(v) {
        this[2] = v;
    }
    /**
     * 向量对象转化为字符串。
     * @method
     */
    toString() {
        return `Vec3(${this.join(',')})`
    }
    /**
     * 向量拷贝。
     * @method
     */
    copy(v) {
        this[0] = v[0];
        this[1] = v[1];
        this[2] = v[2];
        return this;
    }
    /**
     * 当前向量和目标向量是否相等。
     * @method
     */
    equal(v) {
        return (Math.abs(this[0] - v[0]) <= EPSILON) && (Math.abs(this[1] - v[1]) <= EPSILON) && (Math.abs(this[2] - v[2]) <= EPSILON);
    }
    /**
     * 向量克隆。
     * @method
     */
    clone() {
        return new Vec3(this);
    }
    /**
     * 向量加法。
     * @method
     */
    add(v) {
        this[0] += v[0];
        this[1] += v[1];
        this[2] += v[2];
        return this;
    }
    /**
     * 向量减法。
     * @method
     */
    sub(v) {
        this[0] -= v[0];
        this[1] -= v[1];
        this[2] -= v[2];
        return this;
    }
    /**
     * 向量乘法。
     * @method
     */
    multiply(v) {
        this[0] *= v[0];
        this[1] *= v[1];
        this[2] *= v[2];
        return this;
    }
    /**
     * 向量除法。
     * @method
     */
    div(v) {
        this[0] /= v[0];
        this[1] /= v[1];
        this[2] /= v[2];
        return this;
    }
    /**
     * 向量缩放。
     * @method
     */
    scale(v) {
        this[0] *= v;
        this[1] *= v;
        this[2] *= v;
        return this;
    }
    /**
     * 向量反向。
     * @method
    */
    negate() {
        this[0] *= -1;
        this[1] *= -1;
        this[2] *= -1;
        return this;
    }
    /**
     * 计算向量长度。
     * @method
     */
    length() {
        return Math.sqrt(this[0] * this[0] + this[1] * this[1] + this[2] * this[2]);
    }
    /**
     * 计算向量距离。
     * @method
     */
    distance(v) {
        let x = this[0] - v[0], y = this[1] - v[1], z = this[2] - v[2]
        return Math.sqrt(x * x + y * y + z * z);
    }
    /**
     * 向量插值。
     * @param {Number} t 插值参数，0表示起点 1表示终点
     * @method
     */
    lerp(t) {
        this[0] *= t;
        this[1] *= t;
        this[2] *= t;
        return this;
    }
    /**
     * 向量绕X轴旋转。
     * @param {Number} rad 旋转角度。单位:弧度。
     * @method
     */
    rotateX(rad) {
        const py = this[1];
        const pz = this[2];
        //perform rotation
        //translate to correct position
        this[0] = this[0];
        this[1] = (py * Math.cos(rad) - pz * Math.sin(rad));
        this[2] = (py * Math.sin(rad) + pz * Math.cos(rad));
        return this;
    }
    /**
     * 向量绕Y轴旋转。
     * @param {Number} rad 旋转角度。单位:弧度。
     * @method
     */
    rotateY(rad) {
        //Translate point to the origin
        const px = this[0];
        const pz = this[2];
        //perform rotation
        //translate to correct position
        this[0] = (pz * Math.sin(rad) + px * Math.cos(rad));
        this[1] = this[1];
        this[2] = (pz * Math.cos(rad) - px * Math.sin(rad));
        return this;
    }
    /**
     * 向量绕Z轴旋转。
     * @param {Number} rad 旋转角度。单位:弧度。
     * @method
     */
    rotateZ(rad) {
        //Translate point to the origin
        const px = this[0];
        const py = this[1];
        //perform rotation
        //translate to correct position
        this[0] = (px * Math.cos(rad) - py * Math.sin(rad));
        this[1] = (px * Math.sin(rad) + py * Math.cos(rad));
        this[2] = this[2];
        return this;
    }
    /**
     * 计算当前向量到目标向量的夹角。
     * @param {Vec3} v 目标向量。
     * @method
     */
    angle(v = [1, 0, 0]) {
        let ax = this[0],
            ay = this[1],
            az = this[2],
            bx = v[0],
            by = v[1],
            bz = v[2],
            mag = Math.sqrt((ax * ax + ay * ay + az * az) * (bx * bx + by * by + bz * bz)),
            cosine = mag && this.dot(v) / mag;
        return Math.acos(Math.min(Math.max(cosine, -1), 1));
    }
    /**
     * 向量的逆。
     * @method
     */
    inverse() {
        this[0] = 1.0 / this[0];
        this[1] = 1.0 / this[1];
        this[2] = 1.0 / this[2];
        return this;
    }
    /**
     * 向量的绝对值变换。
     * @method
     */
    abs() {
        this[0] = Math.abs(this[0]);
        this[1] = Math.abs(this[1]);
        this[2] = Math.abs(this[2]);
        return this;
    }
    /**
     * 当前向量和目标向量的点积。
     * @param {Vec3} v 目标向量。
     * @method
     */
    dot(v) {
        return this[0] * v[0] + this[1] * v[1] + this[2] * v[2];
    }
    /**
     * 当前向量和目标向量的叉集。
     * @param {Vec3} v 目标向量。
     * @method
     * @reture {Vec3} 返回一个三维向量。
     */
    cross(v) {
        let ax = this[0],
            ay = this[1],
            az = this[2];
        let bx = v[0],
            by = v[1],
            bz = v[2];
        this[0] = ay * bz - az * by;
        this[1] = az * bx - ax * bz;
        this[2] = ax * by - ay * bx;
        return this;
    }
    /**
     * 乘3阶矩阵
     * @method
     */
    transformMat3(m) {
        let x = this[0],
            y = this[1],
            z = this[2];
        this[0] = x * m[0] + y * m[3] + z * m[6];
        this[1] = x * m[1] + y * m[4] + z * m[7];
        this[2] = x * m[2] + y * m[5] + z * m[8];
        return this;
    }
    /**
     * 乘4阶矩阵
     * @method
     */
    transformMat4(m) {
        let x = this[0],
            y = this[1],
            z = this[2];
        let w = m[3] * x + m[7] * y + m[11] * z + m[15];
        w = w || 1.0;
        this[0] = (m[0] * x + m[4] * y + m[8] * z + m[12]) / w;
        this[1] = (m[1] * x + m[5] * y + m[9] * z + m[13]) / w;
        this[2] = (m[2] * x + m[6] * y + m[10] * z + m[14]) / w;
        return this;
    }
    /**
     * 乘四元数
     * @method
     */
    transformQuat(q) {

        const x = this[0], y = this[1], z = this[2];
        const qx = q[0], qy = q[1], qz = q[2], qw = q[3];

        // calculate quat * vector

        const ix = qw * x + qy * z - qz * y;
        const iy = qw * y + qz * x - qx * z;
        const iz = qw * z + qx * y - qy * x;
        const iw = - qx * x - qy * y - qz * z;

        // calculate result * inverse quat

        this[0] = ix * qw + iw * - qx + iy * - qz - iz * - qy;
        this[1] = iy * qw + iw * - qy + iz * - qx - ix * - qz;
        this[2] = iz * qw + iw * - qz + ix * - qy - iy * - qx;

        return this;

    }
    /**
     * 向量归一化。
     * @method
     */
    normalize() {
        let x = this[0];
        let y = this[1];
        let z = this[2];
        let len = x * x + y * y + z * z;
        if (len > 0) {
            //TODO: evaluate use of glm_invsqrt here?
            len = 1 / Math.sqrt(len);
        }
        this[0] *= len;
        this[1] *= len;
        this[2] *= len;
        return this;
    }
    /**
     * 计算贝兹尔曲线
     * @method
     */
    static bezier(a, b, c, d, t) {
        let inverseFactor = 1 - t;
        let inverseFactorTimesTwo = inverseFactor * inverseFactor;
        let factorTimes2 = t * t;
        let factor1 = inverseFactorTimesTwo * inverseFactor;
        let factor2 = 3 * t * inverseFactorTimesTwo;
        let factor3 = 3 * factorTimes2 * inverseFactor;
        let factor4 = factorTimes2 * t;
        let out = new Vec3();
        out[0] = a[0] * factor1 + b[0] * factor2 + c[0] * factor3 + d[0] * factor4;
        out[1] = a[1] * factor1 + b[1] * factor2 + c[1] * factor3 + d[1] * factor4;
        out[2] = a[2] * factor1 + b[2] * factor2 + c[2] * factor3 + d[2] * factor4;
        return out;
    }
}
export { Vec3 }